#include "RpcShell.hpp"
#include "MqttService.hpp"
#include "B64Coder.hpp"
#include <Json/json.h>
#include <openssl/types.h>
#ifndef MqttRpcShellClass
#define MqttRpcShellClass
#define REQUEST "v1/devices/me/rpc/request/"
#define RESPONSE "v1/devices/me/rpc/response/"
class MqttRpcShell : MqttOnMessageCallback
{
private:
    RpcShell *rpcShell;
    MqttConnection *mqttConnection;
    Json::Reader jReader;
    EVP_ENCODE_CTX *eCtx;
    const inline std::string getResponseTopic(const std::string &topic)
    {
        std::string responseId = std::string(topic);
        for (int i = responseId.size() - 1; i > 0; i--)
        {
            if (responseId[i] == '/')
            {
                responseId.erase(0, i + 1);
                break;
            }
        }
        return std::string(RESPONSE).append(responseId);
    }

public:
    MqttRpcShell(MqttConnection *mqttConnection)
    {
        rpcShell = new RpcShell();
        eCtx = EVP_ENCODE_CTX_new();
        this->mqttConnection = mqttConnection;
        mqttConnection->subscribe(REQUEST "+", this);
    }
    ~MqttRpcShell()
    {
        mqttConnection->unsubscribe(REQUEST "+", this);
        delete eCtx;
    }
    virtual const inline void callback(std::string &topic, std::vector<char> &message)
    {
        Json::Value jobj;
        jReader.parse(std::string(message.data()), jobj, false);
        std::string method = jobj["method"].asString();
        // if判断根据调用频率排序
        if (method == "poll")
        {
            std::string id = jobj["params"]["id"].asString();
            std::string res = rpcShell->readShell(id);
            std::vector<unsigned char> data;
            data.resize(res.size());
            memcpy((void *)data.data(), res.data(), data.size());
            Json::Value rjo = Json::objectValue;
            rjo["data"] = B64Coder::base64Encode(data, eCtx);
            std::string pData = rjo.toStyledString();
            mqttConnection->publish(getResponseTopic(topic), rjo.toStyledString());
        }
        else if (method == "send")
        {
            std::string id = jobj["params"]["id"].asString();
            std::string b64Data = jobj["params"]["data"].asString();
            for (int i = b64Data.size() - 1; i >= 0; i--)
            {
                if (b64Data[i] == '-')
                {
                    b64Data[i] = '=';
                }
                else
                    break;
            }
            std::vector<unsigned char> data = B64Coder::base64Decode(b64Data, eCtx);
            std::string wData;
            wData.resize(data.size());
            memcpy((void *)wData.c_str(), data.data(), wData.size());
            rpcShell->writeShell(wData, id);
        }
        else if (method == "login")
        {
            std::string id = rpcShell->login();
            Json::Value rjo = Json::objectValue;
            rjo["id"] = id;
            mqttConnection->publish(getResponseTopic(topic), rjo.toStyledString());
        }
        else if (method == "logout")
        {
            std::string id = jobj["params"]["id"].asString();
            rpcShell->logout(id);
        }
    }
};
#endif